package com.hmdp.service.impl;

import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.hmdp.dto.Result;
import com.hmdp.entity.SeckillVoucher;
import com.hmdp.entity.VoucherOrder;
import com.hmdp.mapper.VoucherOrderMapper;
import com.hmdp.service.ISeckillVoucherService;
import com.hmdp.service.IVoucherOrderService;
import com.hmdp.utils.RedisIdWorker;
import com.hmdp.utils.UserHolder;
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.aop.framework.AopContext;
import org.springframework.core.io.ClassPathResource;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import java.time.LocalDateTime;
import java.util.Collections;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

import static com.hmdp.utils.RedisConstants.SECKILL_ORDER_KEY;

/**
 * <p>
 * 服务实现类
 * </p>
 *
 * @author 虎哥
 * @since 2021-12-22
 */
@Slf4j
@Service
public class VoucherOrderServiceImpl extends ServiceImpl<VoucherOrderMapper, VoucherOrder> implements IVoucherOrderService {

    @Resource
    private ISeckillVoucherService seckillVoucherService;

    @Resource
    private RedisIdWorker redisIdWorker;

    @Resource
    private StringRedisTemplate stringRedisTemplate;

    @Resource
    private RedissonClient redissonClient;

    private static final DefaultRedisScript<Long> SECKKILL_KEY_SCRIPT;

    static {
        SECKKILL_KEY_SCRIPT = new DefaultRedisScript<>();
        SECKKILL_KEY_SCRIPT.setLocation(new ClassPathResource("seckkill.lua"));
        SECKKILL_KEY_SCRIPT.setResultType(Long.class);
    }

    /**
     * 阻塞队列
     */
    private BlockingQueue<VoucherOrder> ordersQueue = new ArrayBlockingQueue<>(1024*1024);

    private static final   ExecutorService seckill_order_handler= Executors.newSingleThreadExecutor();
    private  IVoucherOrderService proxy;
    @PostConstruct
    private void init (){
        seckill_order_handler.submit(new VocherOrderHandler());
    }

    private class VocherOrderHandler implements Runnable{

        @Override
        public void run() {
            while ( true){
                try {
                    // 获取队列中的订单信息
                    VoucherOrder take = ordersQueue.take();
                    // 创建订单
                    handlerVoucherOrder(take);
                } catch (InterruptedException e) {
                   log.error("处理订单异常",e);
                }
            }
        }
    }

    private void handlerVoucherOrder(VoucherOrder take) {
        Long userId = take.getId();
        RLock lock = redissonClient.getLock(SECKILL_ORDER_KEY + userId);

        // 获取锁
        boolean lockFlag = lock.tryLock();
        if (!lockFlag){
            log.error("不允许重复下单");
            return;
        }
        try {

//            return proxy.createVoucherOrder(take.getVoucherId(),take.getId());
             proxy.createVoucherOrder1(take);
        } finally {
            lock.unlock();
        }
    }

    @Override
    public Result seckillVoucher(Long voucherId) {
        Long id = UserHolder.getUser().getId();
        // 执行lua脚本
        Long result = stringRedisTemplate.execute(
                SECKKILL_KEY_SCRIPT,
                Collections.emptyList(),
                voucherId.toString(),
                id.toString()
        );
        int ret = result.intValue();
        // 判断结果是否为0
        if (ret != 0){
            // 不为0 代表没有购买资格
            return Result.fail(ret == 1 ? "库存不足" : "不能重复下单");
        }

        // 为0 代表有购买资格
        long orderId = redisIdWorker.nextId("order");
        // todo 保存到阻塞队列 中
        VoucherOrder voucherOrder = new VoucherOrder();
        // 订单ID
        voucherOrder.setId(orderId);
        voucherOrder.setUserId(id);
        // 代金券ID
        voucherOrder.setVoucherId(voucherId);
        // 下单
        ordersQueue.add(voucherOrder);

        // 获取代理对象
         proxy = (IVoucherOrderService) AopContext.currentProxy();
        // 返回订单ID
        return Result.ok(0);
    }

    // 分布式解决方案
    @Deprecated
    //    @Override
    public Result seckillVoucher2(Long voucherId) {
        // TODO: 秒杀券实现
        // 用户ID

        // 查询优惠券信息
        SeckillVoucher voucherData = seckillVoucherService.getById(voucherId);
        // 判断秒杀是否开始 或者结束
        LocalDateTime beginTime = voucherData.getBeginTime();
        if (!beginTime.isBefore(LocalDateTime.now())) {
            return Result.fail("秒杀尚未开始");
        }
        LocalDateTime endTime = voucherData.getEndTime();
        if (!endTime.isAfter(LocalDateTime.now())) {
            return Result.fail("秒杀已结束");
        }
        // 判断库存是否充足
        Integer stock = voucherData.getStock();
        if (stock <= 0) {
            return Result.fail("库存不足");
        }
        Long id = UserHolder.getUser().getId();
       // 创建锁对象
//        SimpleRedisLock simpleRedisLock = new SimpleRedisLock(stringRedisTemplate,SECKILL_ORDER_KEY + id );
        // 使用redissonClient 对象获取锁对象
        RLock lock = redissonClient.getLock(SECKILL_ORDER_KEY + id);

        // 获取锁
        boolean lockFlag = lock.tryLock();
        if (!lockFlag){
            return Result.fail("正在处理中");
        }
        try {
            IVoucherOrderService proxy = (IVoucherOrderService) AopContext.currentProxy();
            return proxy.createVoucherOrder(voucherId,id);
        } finally {
            lock.unlock();
        }

    }


    // 这是单体架构的解决方案
    @Deprecated
//    @Override
    public Result seckillVoucher1(Long voucherId) {
        // 用户ID

        // 查询优惠券信息
        SeckillVoucher voucherData = seckillVoucherService.getById(voucherId);
        // 判断秒杀是否开始 或者结束
        LocalDateTime beginTime = voucherData.getBeginTime();
        if (!beginTime.isBefore(LocalDateTime.now())) {
            return Result.fail("秒杀尚未开始");
        }
        LocalDateTime endTime = voucherData.getEndTime();
        if (!endTime.isAfter(LocalDateTime.now())) {
            return Result.fail("秒杀已结束");
        }
        // 判断库存是否充足
        Integer stock = voucherData.getStock();
        if (stock <= 0) {
            return Result.fail("库存不足");
        }
        Long id = UserHolder.getUser().getId();
        // 添加悲观锁 id.toString().intern() 当用户ID的值一样时 锁不变
        synchronized(id.toString().intern()) {
            // 在这里需要拿到事务的代理对象 因为这个方法没有加上事务注解 等于是 当前类的this去调用该方法 他的动态代理已经失效了 所以要自己获取这个动态代理对象
//            IVoucherOrderService proxy = (IVoucherOrderService) this; this 指向当前实例对象
            // 获取事务代理对象
            IVoucherOrderService proxy = (IVoucherOrderService) AopContext.currentProxy();
            return proxy.createVoucherOrder(voucherId,id);
        }
    }



    @Transactional()
    public  Result createVoucherOrder(Long voucherId, Long id) {

            // 一人一单
            int count = query().eq("user_id", id).eq("voucher_id", voucherId).count();
            if (count>0){
                return Result.fail("您已购买过");
            }

            // 扣减库存
            boolean flag = seckillVoucherService.update()
                    .setSql("stock = stock - 1") // 解决超卖问题
                    .eq("voucher_id", voucherId)
                    .gt("stock",0)
                    .update();
            // 创建订单
            if (!flag){
                // 扣减失败
                return Result.fail("库存不足");
            }
            // 返回
            VoucherOrder voucherOrder = new VoucherOrder();
            // 订单ID
            long orderId = redisIdWorker.nextId(SECKILL_ORDER_KEY);
            voucherOrder.setId(orderId);

            voucherOrder.setUserId(id);
            // 代金券ID
            voucherOrder.setVoucherId(voucherId);
            save(voucherOrder);
            return Result.ok(orderId);


    }

    @Override
    @Transactional
    public void createVoucherOrder1(VoucherOrder take) {
        Long userId = take.getId();
        Long voucherId = take.getVoucherId();

        // 一人一单
        int count = query().eq("user_id", userId).eq("voucher_id", voucherId).count();
        if (count>0){
            log.error("您已购买过");
        }

        // 扣减库存
        boolean flag = seckillVoucherService.update()
                .setSql("stock = stock - 1") // 解决超卖问题
                .eq("voucher_id", voucherId)
                .gt("stock",0)
                .update();
        // 创建订单
        if (!flag){
            // 扣减失败
            log.error("库存不足");
        }

    }
}
